home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / aminet / demo / euro / rot2.lha / rot.description < prev    next >
Text File  |  1993-04-02  |  14KB  |  288 lines

  1.  
  2. 3DGame Engine, by: Jason Freund and Gabe Dalbec.
  3.  
  4.  
  5.  
  6.  
  7.                                 INTRO
  8.  
  9.     This paper describes the algorithm used to create the demo, the
  10. current problems with it, and possiblities for future expansion.  It's
  11. intended for curious people or for people who might want to join a team
  12. to develop this into a PD game (see rot.invite file) or people curious
  13. about the technical aspects of the program.
  14.  
  15.  
  16.  
  17.                              CURRENT WORK
  18.  
  19.     I've done most of the current work myself and with a new partner,
  20. Chris Hames (Degrader, DirWorks, PCTask).  My roommate who did half the
  21. original work is in Mexico for a few months.
  22.  
  23.     First, the perspective problem, described below.  The first thing
  24. you notice about the demo is that walls get stretched when they are
  25. close to you and at an angle.
  26.  
  27.     We are also currently working on objects.  I am not going to use
  28. my custom blitter routines to blit objects on the screen -- like I
  29. originally planned.  Instead, objects will be converted to the same
  30. semi-chunky format (described below) as the walls and then rendered
  31. into fast ram.  The advantages are: 1) no need to double buffer to
  32. get rid of flickering caused by 2 stage drawing (one for walls, one
  33. for objects).  This is a good speedup.  2) Faster on high end computers.
  34. (Which is my target machine, so I don't care)  3) Can do correct
  35. vertical and horizontal scaling.  -- During rendering, I can pull
  36. columns and rows from the brush to shrink it to fit perfectly.  If I'd
  37. used the blitter, I could only shrink vertically and I'd have to use
  38. 5 different sizes of each brush to get choppy scaling in the H-dir.
  39. Disadvantage of not using blitter: objects restricted to 16 colors
  40. because of nature of semi-chunky format.
  41.  
  42.  
  43.  
  44.                         OVERVIEW OF THE ALGORITHM
  45.  
  46.     This program doesn't use any texture mapping or ray tracing.
  47. Everything is faked.  There are many limitations on the engine which
  48. allow us to fake things. First, walls are all equal-length and equal
  49. height. They can only be spaced multiples of 1 wall length apart.  You
  50. cannot see through walls. Walls must be perpendicular to each other.
  51. The whole dungeon is vertically symmetrical.  These assumptions allow us
  52. to generate each frame in "screen" space -- by looking at the start and
  53. endpoints of the tops of each wall face.
  54.  
  55.     To reduce the number of rotations per frame, the dungeon data is in
  56. third normal form.  That means we have a list of (X,Y,Z) points
  57. representing endpoints of wall faces and a list of edges that reference
  58. this list of points.  We also create a list of midpoints from the list
  59. of edges.   The Z values of each point (or height of the walls) are a
  60. +constant (ie 0.6). This way each edge represents the top line of a
  61. wall. All you need is this top edge to compute the "zbuffer".
  62.  
  63.     Each frame is defined by a 1 line "z-buffer": typedef struct {
  64.     short height;
  65.     short bitmap;
  66.     short offset;
  67.     short junk;   /* to make structure 2^3 long */ } mapline; mapline
  68. zbuffer[SCREENWIDTH];
  69.  
  70. Now, for each 1 pixel column on the screen, you have:
  71.  
  72. 1) height == how far from the top of the screen to start drawing the
  73. column. You draw from height, down to the center of the screen. 2)
  74. bitmap == which source bitmap number to draw from at that column. 3)
  75. offset == How far in the source bitmap to go to grab the column to draw.
  76.  
  77.     For each frame you generate a new "zbuffer" by examining every point
  78. between the start and endpoint of every edge (that you can at least
  79. partially see) in the dungeon.  First, set height=MIDDLEOFSCREEN for
  80. every column in the zbuffer.  I use integer Breshenham's to generate
  81. every point between the endpoints of the line segment.  Then for each
  82. point on the line, check to see if the Y coord is higher than the
  83. corresponding zbuffer[i].height.  If it is, then that point represents a
  84. column that will be in front of everything else and you should update
  85. every field in zbuffer[i].  When you've done this for every edge, your
  86. zbuffer will contain everything you need to describe the scene.
  87.  
  88.     Once you generate the zbuffer, just loop through the array, yank
  89. _column from _bitmap and draw it from _height.  Since you know how high
  90. the wall is, you can calculate how much to shrink or expand that source
  91. bitmap column and skip or redraw rows of the column right in the loop
  92. that copies it to the screen.
  93.  
  94.  
  95. COPY-OUT
  96.  
  97.     The "copy-out" routine is the biggest bottleneck in the program.
  98. The "copy-out" routine copies a column from the source bitmap to the
  99. screen, shrinking or expanding it as it copies.  I will spend some time
  100. writing about this aspect of the program since it is the most important
  101. part.
  102.     To begin with, it is the most optimized assembly we've ever written.
  103. It uses lookup tables for every calculation where using a lookup table
  104. will save a cycle. This is the routine where we made huge changes to our
  105. code to implement schemes that would remove 1 cycle from the innermost
  106. loop.  We tried about a half dozen schemes before we came up with our
  107. final version.
  108.  
  109.     First, our bitmaps are preconverted to 1 big chunky plane rather
  110. than 4 small bitplanes; the first ulong represents the first 8 pixels.
  111. Consequently, when you want to display a pixel, you do shifts, ands, and
  112. ors a longword at a time and get the bits for all 4 bitplanes at once.
  113. These calculations are all done in FAST memory and rendered onto a FAST
  114. memory screen-sized area. Then, when you're all done rendering the
  115. screen, you copy it all to planar-mode CHIP memory so you can see it.
  116.  
  117.     It's convenient that the 3000's memory and bus are all four bytes
  118. wide because that gives us all the colors we could ask for: 16.  But
  119. we're getting 32 colors for almost free.  The only cost is blit-clearing
  120. a fifth bitplane (half the size of the screen), 20% bus contention with
  121. denise chip which is in competition for bus time to display the picture,
  122. and expanding our copy-out loop by one to write a 1 or 0 into the fifth
  123. plane according to which palette is being used in that column.  It turns
  124. out that all these costs don't add up to more than about 5% slowdown.
  125. So we took it. The "source bitmap number" field of our mapline is also
  126. an index into an array that tells us which palette (0 or 1) the bitmap
  127. uses. Our "copy-out" routine sets the appropriate bit in the fifth
  128. bitplane of the destination screen based on this lookup without taking
  129. any time away from the longword logical operations on the source bitmap.
  130. We used to employ the blitter to fill the fifth bitplane in one fell
  131. swoop instead of filling it byte-by-byte withe the CPU.  This was done
  132. by drawing vertical lines everywhere the scene changed palette.  Then
  133. we'd fill the screen, telling the blitter to change fill modes every
  134. time it hit a line.  Not only was this slower, but we got a slight
  135. flickering when we drew the fifth bitplane with blitlines and a blitfill
  136. due to the fact that we were drawing to the screen at 2 different times.
  137. But when we started to fill the fifth bitplane inside the copy-out loop,
  138. the flickering was fixed.
  139.  
  140.     One of the copy-out methods we experimented with was a pipelined
  141. engine. I am describing it because it would be useful for people who
  142. want to modify our engine to run on the 500 which doesn't have a fast
  143. CPU.  You can use the CPU to generate the zbuffer while the blitter
  144. clears the screen.  Then the CPU would perform the copy-out.  Then the
  145. Blitter would do the Vflip while the CPU rotated the points for the next
  146. frame, and so on.  We rejected this and other schemes because a 68030
  147. CPU is faster than the blitter for everything; any parallelism would not
  148. speed up the pipeline.  In fact, after everything was optimized for the
  149. CPU, we didn't need the blitter at all.
  150.  
  151.  
  152.  
  153.                                PROBLEMS:
  154.  
  155.     One problem we found is that at the corners of the walls, a few
  156. pixels from a wall behind would overlap the wall in front. This ocurred
  157. because we're not using any painter's algorithm to traverse the list of
  158. edges.  At the corners where both walls at a corner are at the same
  159. height, there is no way to judge which is actually in front.  Another
  160. problem we had is that corners didn't exactly line up because we were
  161. using breshenhams to calculate points along a line segment.
  162.  
  163.     Both of these problems were fixed by multiplying every Y value by 32
  164. before calculating points between the endpoints of the edge and then
  165. dividing by 32 after the zbuffer is generated.  Be sure to check for -Y
  166. values before lsl/r #5's to preserve the sign of Y.
  167.  
  168.     Another problem that we still have is with the perspective of the
  169. walls. Flat walls or walls far away are fine.   But walls that are close
  170. to you get smeared. This is because we don't calculate any real
  171. perspective.  We just use a linear mapping based on the ratio of the
  172. width of the rotated wall to the width of the flat, original source
  173. bitmap to calculate offsets to the source bitmap.   Walls that are at a
  174. steep angle and close to you end up drawing from a very small portion
  175. from the source bitmap.
  176.  
  177.     One solution that we tried was to generate real perspective based on
  178. a recursive algorithm.  We already have a list of midpoints for each
  179. wall face that we use to detect wall collisions.  If we rotate these
  180. midpoints, we can find the ratio between the width on the screen of the
  181. close half of the wall to the width of the far half of the wall.  This
  182. midpoint will draw from the center of the source bitmap.  Then we just
  183. recursively subdide each half of the wall, keeping track of the new
  184. corresponding center on the source bitmap as we go, building an array of
  185. source bitmap offsets.  This is slow, but could be precomputed and
  186. simulated with a lookup table that just takes in a ratio to lookup
  187. offsets for the largest possible wall. Smaller walls could then use a
  188. linear mapping from this large wall. Unfortunately, we still haven't
  189. been able to get the recursive routine to terminate correctly.
  190.  
  191.  
  192.  
  193.  
  194.                                SPEEDUPS:
  195.  
  196.     Our first speedup was to only draw the top half of the screen and
  197. use the copper to reflect the top half onto the bottom half. Actually,
  198. this was just changed by Chris Hames to be done with the CPU now.  But
  199. we still get a cute floor and ceiling for free out of this by changing
  200. the background colors at certain rows.  This speedup makes the program a
  201. good 35% faster than drawing all the way down.
  202.  
  203.     Resolution switching was implemented.  Press "m" to change modes.
  204. Default is auto-switch.  When you run, your eyeballs can't see detail
  205. as clearly -- so we make pixels chunky.  When you rotate fast or run,
  206. the program draws frames in 1/4 -lores.  THen when you stop running,
  207. it returns to regular lores.  This is done by creating fastram image
  208. 1/2 width, 1/2 height of normal -- and then blowing it up during copy
  209. out to chip.
  210.  
  211.     The list of (X,Y) coordinates representing the endpoints of wall
  212. faces is sorted by X coordinate.  Wall collisions are handled by
  213. checking every point in the maze.  If any point is inside the box you
  214. are about to move into, an X and/or Y component of your movement is set
  215. to 0 so that you slide along the edge of a wall. To make the check
  216. against every point fast for large dungeons, we sort the list of points
  217. by X coordinate and only check the sublist until the X coordinate is out
  218. of the range of your box.
  219.  
  220.  
  221.  
  222.                       AREAS FOR FUTURE EXPANSION
  223.  
  224.     Ideally, if enough people support this, it could develop into a
  225. fully multitasking game that runs on all amigas, as fast as your amiga
  226. can make it go.
  227.  
  228.     I decided not to do windows or 2 story buildings like in the game
  229. Legends of Valor.  I'd have to do a bit of backtracking to implement
  230. them and I probably won't have time.
  231.  
  232.     Aside from obvious things that this needs to develop into a game
  233. like sound, music, good graphics, maps, monsters, etc, etc, some
  234. technical advances might be:
  235.  
  236. AGA:
  237.  
  238.     Only the 4000 has enough spare cycles to handle this.  AGA's
  239. 256 color mode conveniently fits twice the length of my 1longword
  240. chunky mode.  So there'd be no wasted cycles in making the game
  241. work with 256 colors.
  242.  
  243.  
  244. CHANGE SCREENSIZE:
  245.  
  246.     I'd like to be able to adjust the view window like in wolf3d.
  247. This way, you could get it to run fast on an 020 w/ FPU and get it
  248. to run on a bigger view window on the 040.
  249.  
  250.  
  251. A500 COMPATABILITY:
  252.  
  253.     This program was not meant for machines without a fast CPU or a math
  254. chip. We don't have access to a 500 and we have no particular desire to
  255. see it run fast on one.  We'd be happy to turn our sourcecode over to
  256. anyone who can read C and can program real well on the 500.
  257.  
  258.     Some things that would have to be done are:
  259.  
  260. 1) Shrink the screensize.  I think one-quarter screen would be small
  261. enough. Our engine code is modular enough to make adjusting the screen
  262. size easy. 2) Rewrite several routines to use integer math.  I'm sure
  263. eurodemo coders have a lot of experience there.  Integer math may even
  264. help the A3000 version. 3) Vflip screen with blitter instead of CPU.
  265. Use parallel processing where-ever possible (see COPY-OUT, above). 4)
  266. The two 16 color palette trick is done with the CPU.  It would have to
  267. be redone using vertical lines and 1 blitter fill.  Actually, this is
  268. the way we originally did it, so the code is already there -- just
  269. commented out.  Of course, using the blitter, as explained in COPY-OUT,
  270. above, will cause flickering because you are drawing once for the top
  271. four plains and then again later for the fifth plane.  This could be
  272. fixed, however, with doublebuffering which you'll probably need anyway
  273. on the 500.
  274.  
  275.  
  276. ---------------------------------------------------------------------------
  277. School addresss (until 6/12/93):        Permanent Address
  278.  
  279. Jason Freund,                              Jason Freund 2033 F Street #311
  280. 690 Erie Dr Davis, CA 95616                Sunnyvale, CA  94087
  281. 916-758-0272                            408-732-0421
  282.  
  283.  
  284. Gabe Dalbec                                Gabe Dalbec 2033 F Street #311
  285. 789 Butterfly Rd Davis, CA  95616        Quincy, CA 95971
  286. 916-758-0272                            916-281-6600
  287.  
  288.